using System;
using System.Collections.Generic;

using Pegasus.Diagnostics;
using Pegasus.Runtime.Serialization;

namespace Pegasus.Workflow.Service
{
	/// <summary>
	/// The default tracking service and base class for custom tracking services 
	/// </summary>
	public abstract class WorkflowTrackingService : IWorkflowService
	{
		// Local Instance Values
		private WorkflowService m_workflowService = null;

		/// <summary>
		/// Initializes a new instance of the <see cref="T:WorkflowTrackingService"/> class.
		/// </summary>
		public WorkflowTrackingService()
		{
		}

		/// <summary>
		/// Gets the workflow service that this service is attached to.
		/// </summary>
		/// <value>The workflow service.</value>
		public virtual WorkflowService WorkflowService
		{
			get
			{
				return m_workflowService;
			}
		}

		/// <summary>
		/// Starts this instance of the service.
		/// </summary>
		/// <param name="workflowService">The workflow service that owns this service.</param>
		public virtual void Start( WorkflowService workflowService )
		{
			// Check Parameters
			ParamCode.AssertNotNull( workflowService, "workflowService" );

			m_workflowService = workflowService;
		}

		/// <summary>
		/// Stops this instance of the service.
		/// </summary>
		public virtual void Stop()
		{
			m_workflowService = null;
		}

		/// <summary>
		/// Records the workflow started.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="startupArgs">The startup args.</param>
		public virtual void RecordWorkflowStarted( WorkflowContext context, object[] startupArgs )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );
			ParamCode.AssertNotNull( startupArgs, "startupArgs" );

			// Record tracking event
			RecordEvent( TrackingActions.WorkflowStarted, context, null, ArgumentArrayToXml( startupArgs ) );
		}

		/// <summary>
		/// Records the restarted message.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="startState">The start state.</param>
		public virtual void RecordRestartedMessage( WorkflowContext context, string startState )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );
			ParamCode.AssertNotEmpty( startState, "startState" );

			// Record tracking event
			RecordEvent( TrackingActions.WorkflowRestarted, context, null, SerializationHelper.ObjectToXml( startState ) );
		}

		/// <summary>
		/// Records the workflow completed.
		/// </summary>
		/// <param name="context">The context.</param>
		public virtual void RecordWorkflowCompleted( WorkflowContext context )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );

			// Record tracking event
			RecordEvent( TrackingActions.WorkflowCompleted, context, null, SerializationHelper.ObjectToXml( context.Workflow.ExitCode ) );
		}

		/// <summary>
		/// Records the data exchange message.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="message">The message.</param>
		public virtual void RecordDataExchangeMessage( WorkflowContext context, WorkflowMessageService.DataExchangeMessage message )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );
			ParamCode.AssertNotNull( message, "message" );

			// Record tracking event
			RecordEvent( TrackingActions.DataExchangeMessage, context, null, SerializationHelper.ObjectToXml( message ) );
		}

		/// <summary>
		/// Records the workflow event message.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="message">The message.</param>
		public virtual void RecordWorkflowEventMessage( WorkflowContext context, WorkflowMessageService.WorkflowEventMessage message )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );
			ParamCode.AssertNotNull( message, "message" );

			// Record tracking event
			RecordEvent( TrackingActions.WorkflowEventMessage, context, null, SerializationHelper.ObjectToXml( message ) );
		}

		/// <summary>
		/// Records the custom message.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="messageName">Name of the message.</param>
		/// <param name="args">The args.</param>
		public virtual void RecordCustomMessage( WorkflowContext context, string messageName, params object[] args )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );
			ParamCode.AssertNotEmpty( messageName, "messageName" );
			ParamCode.AssertNotNull( args, "args" );

			// Record tracking event
			RecordEvent( TrackingActions.CustomMessage, context, messageName, ArgumentArrayToXml( args ) );
		}

		/// <summary>
		/// Records the custom message.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="messageName">Name of the message.</param>
		/// <param name="xml">The XML.</param>
		public virtual void RecordCustomMessageXml( WorkflowContext context, string messageName, string xml )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );
			ParamCode.AssertNotEmpty( messageName, "messageName" );
			ParamCode.AssertNotEmpty( xml, "xml" );

			// Record tracking event
			RecordEvent( TrackingActions.CustomMessage, context, messageName, xml );
		}

		/// <summary>
		/// Records the enter state event.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="stateName">Name of the state.</param>
		/// <param name="previousState">State of the previous.</param>
		public virtual void RecordStateEntered( WorkflowContext context, string stateName, string previousState )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );

			// Build parameters list
			string[] states = new string[] { stateName, previousState };

			// Record tracking event
			RecordEvent( TrackingActions.StateEntered, context, null, SerializationHelper.ObjectToXml( states ) );
		}

		/// <summary>
		/// Records the exit state event.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="stateName">Name of the state.</param>
		/// <param name="nextState">State of the next.</param>
		public virtual void RecordStateExited( WorkflowContext context, string stateName, string nextState )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );

			// Build parameters list
			string[] states = new string[] { stateName, nextState };

			// Record tracking event
			RecordEvent( TrackingActions.StateExited, context, null, SerializationHelper.ObjectToXml( states ) );
		}

		/// <summary>
		/// Records the exception.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="e">The e.</param>
		public virtual void RecordException( WorkflowContext context, Exception e )
		{
			// Check Parameters
			ParamCode.AssertNotNull( context, "context" );

			string xml = null;

			// Is the exception (and all inner exceptions serializable)
			if( ExceptionHelper.IsExceptionSerializable( e ) )
			{
				xml = SerializationHelper.ObjectToXml( e );
			}
			else
			{
				xml = SerializationHelper.ObjectToXml( ExceptionHelper.DumpExceptionAll( e ) );
			}

			// Record tracking event
			RecordEvent( TrackingActions.Exception, context, null, xml );
		}

		/// <summary>
		/// Records the event.
		/// </summary>
		/// <param name="action">The action.</param>
		/// <param name="context">The context.</param>
		/// <param name="messageName">Name of the message.</param>
		/// <param name="parametersXml">The parameters XML.</param>
		protected abstract void RecordEvent( TrackingActions action, WorkflowContext context, string messageName, string parametersXml );

		/// <summary>
		/// Determines whether [is object serializable] [the specified obj].
		/// </summary>
		/// <param name="obj">The obj.</param>
		/// <returns>
		/// 	<c>true</c> if [is object serializable] [the specified obj]; otherwise, <c>false</c>.
		/// </returns>
		protected bool IsObjectSerializable( object obj )
		{
			if( obj != null )
			{
				return obj.GetType().IsSerializable;
			}

			return true;
		}

		/// <summary>
		/// Adds the argument array.
		/// </summary>
		/// <param name="arguments">The arguments.</param>
		/// <returns></returns>
		private string ArgumentArrayToXml( object[] arguments )
		{
			// Check Parameters
			ParamCode.AssertNotNull( arguments, "arguments" );

			int count = arguments.Length;
			object[] objectList = new object[ count ];

			for( int x = 0; x < count; x++ )
			{
				object arg = arguments[ x ];
				if( IsObjectSerializable( arg ) )
				{
					objectList[ x ] = arg;
				}
				else
				{
					objectList[ x ] = string.Format( "Unserializable object type {0}, {1}", arg.GetType(), arg.ToString() );
				}
			}

			return SerializationHelper.ObjectToXml( objectList );
		}

		/// <summary>
		/// This is the differt tracking events.
		/// </summary>
		protected enum TrackingActions : int
		{
			/// <summary>
			/// 
			/// </summary>
			Unknown = 0,


			/// <summary>
			/// 
			/// </summary>
			WorkflowStarted = 1,

			/// <summary>
			/// 
			/// </summary>
			Reserved_2 = 2, // WorkflowLoaded 

			/// <summary>
			/// 
			/// </summary>
			Reserved_3 = 3, // WorkflowUnloaded 

			/// <summary>
			/// 
			/// </summary>
			WorkflowCompleted = 4,

			/// <summary>
			/// 
			/// </summary>
			DataExchangeMessage = 5,

			/// <summary>
			/// 
			/// </summary>
			WorkflowEventMessage = 6, // WorkflowTimerEvent

			/// <summary>
			/// 
			/// </summary>
			CustomMessage = 7,


			/// <summary>
			/// 
			/// </summary>
			StateEntered = 8,

			/// <summary>
			/// 
			/// </summary>
			StateExited = 9,

			/// <summary>
			/// 
			/// </summary>
			reserved_10 = 10, // StateCustomEvent 

			/// <summary>
			/// 
			/// </summary>
			Exception = 11,

			/// <summary>
			/// 
			/// </summary>
			WorkflowRestarted = 12,
		}		
	}
}
